#include "GribVariable.h"

namespace OCPP
{
	class GridTimeVariable : public GridVariable
	{
	public:
		GridTimeVariable(GribTimeVec vec)
		{
			_vec = vec;
		}

		String getTimeString(int index)
		{
			return _vec[index].toString();
		}

		FloatVector readFloat() 
		{ 
			FloatVector fv;
			for (const auto& t : _vec)
			{
				auto [t1, t2] = t._timeBound;
				fv.push_back(t1);
			}
			return fv; 
		}
		GribTimeVec _vec;
	};

	class GridLevelVariable : public GridVariable
	{
	public:
		GridLevelVariable(GribLevelIndexVec vec)
		{
			_vec = vec;
		}

		String getUnitsString() 
		{ 
			auto [gribLevel,index] = _vec[0];
			return gribLevel.levelUnits;
		}

		FloatVector readFloat()
		{
			FloatVector fv;
			for (const auto& t : _vec)
			{
				auto [t1, t2] = t;
				auto [l1, l2] = t1._levelBound;
				fv.push_back(l1);
			}
			return fv;
		}

		GribLevelIndexVec _vec;
	};

	class GridLonLatVariable : public GridVariable
	{
	public:

	};

	void GribVariable::insert(std::shared_ptr<InventoryEx> inv)
	{
		if (mName.empty())
		{
			mName = inv->comment;
		}

		time_t t1 = inv->refTime + inv->foreSec;
		time_t t2 = inv->validTime;
		double level1 = inv->level;
		double level2 = inv->level2;

		auto& gribLevelIndexVec = _gribTimeMap[{t1, t2}];

		GribLevel gribLevel = {level1,level2};
		gribLevel.units = inv->unitName;
		gribLevel.levelUnits = inv->levelUnit;

		gribLevelIndexVec.push_back({ gribLevel , mInventoryVector.size() });
		mInventoryVector.push_back(inv);
	}

	GribTimeVec GribVariable::getTimeVec()
	{
		GribTimeVec vec;

		for (const auto& it : _gribTimeMap)
		{
			vec.push_back(it.first);
		}

		return vec;
	}

	GridVariablePtr GribVariable::findDimVariable(String name)
	{
		if (name == "time")
		{
			return std::make_shared<GridTimeVariable>(getTimeVec());
		}
		else if (name == "level")
		{
			return std::make_shared<GridLevelVariable>(getLevelVec(_gribTimeMap.begin()->first));
		}
		return std::make_shared<GridLonLatVariable>();
	}

	StringVector GribVariable::getDims()
	{
		return {
			"time",
			"level",
			"longitude",
			"latitude"
		};
	}

	GridShape GribVariable::getShape()
	{
		size_t timeCount = _gribTimeMap.size();
		size_t levelCount = _gribTimeMap.begin()->second.size();
		return {
			timeCount,
			levelCount,
			_lonValues.size(),
			_latValues.size(),
		};
	}

	void GribVariable::afterSetDimXY(VarData& varData)
	{

	}

	const GribLevelIndexVec& GribVariable::getLevelVecByTime(int i)
	{
		auto time = getTimeVec()[i];
		return _gribTimeMap[time];
	}

	const GribLevelIndexVec& GribVariable::getLevelVec(GribTime time)
	{
		return _gribTimeMap[time];
	}

	const String GribVariable::description()
	{
		if (mInventoryVector.empty())
		{
			return "-\n";
		}
		InventoryEx* inv = mInventoryVector[0].get();

		size_t timeSize = getTimeSize();
		size_t levelSize = _gribTimeMap.begin()->second.size();

		StringStream sstream;
		sstream << getName() << "(";
		sstream << "time=" << timeSize << ", ";
		sstream << "level=" << levelSize << ") ";
		sstream << "[" << inv->Grib2_Parameter[0] << ","
			<< inv->Grib2_Parameter[1] << ","
			<< inv->Grib2_Parameter[2] << ","
			<< inv->Grib2_Parameter[3];
		sstream << "] unit:" << inv->unitName;

		if (timeSize * levelSize != mInventoryVector.size())
		{
			sstream << " has invalid dimansion("
				<< mInventoryVector.size() << ")";
		}

		return sstream.str();
	}
}